/*
 * Copyright 2008 FBK (http://www.fbk.eu/)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.fbk.it.hlt.jlsi;

import java.io.IOException;
import java.io.LineNumberReader;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.Writer;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;

import org.apache.log4j.Logger;

/**
 * TO DO
 * 
 * @author Claudio Giuliano
 * @version %I%, %G%
 * @since 1.0
 * @see TermIndex
 */
public class TermIndex {
	/**
	 * Define a static logger variable so that it references the Logger instance
	 * named <code>TermIndex</code>.
	 */
	static Logger logger = Logger.getLogger(TermIndex.class.getName());

	/**
	 * A prototype for a feature factory such that only one instance class can
	 * ever exist.
	 */
	private static TermIndex termIndex;

	/**
	 * to do.
	 */
	private SortedMap map;

	/**
	 * to do.
	 */
	private int count;

	/**
	 * Constructs a <code>TermIndex</code> object.
	 */
	private TermIndex() {
		this(0);
	} // end constructor

	/**
	 * Constructs a <code>TermIndex</code> object.
	 */
	private TermIndex(int count) {
		logger.info("TermIndex " + count);
		map = new TreeMap();
		this.count = count;
	} // end constructor

	/**
	 * Returns the <i>index</i> of the specified token and adds the token to the
	 * termIndex if it is not present yet.
	 * 
	 * @param token
	 *            the token.
	 * @return the <i>index</i> of the specified token.
	 */
	public int put(String token) {
		// logger.debug("TermIndex.put : " + token + "(" + count + ")");
		Integer index = (Integer) map.get(token);

		if (index == null) {
			index = new Integer(count++);
			map.put(token, index);
		}

		return index.intValue();
	} // end get

	/**
	 * Returns the <i>index</i> of the specified token and adds the token to the
	 * termIndex if it is not present yet.
	 * 
	 * @param token
	 *            the token.
	 * @return the <i>index</i> of the specified token.
	 */
	public int getIndex(String token) {
		// logger.debug("TermIndex.get : " + token + "(" + count + ")");
		Integer index = (Integer) map.get(token);

		if (index == null)
			return -1;

		return index.intValue();
	} // end get

	//
	public Set termSet() {
		return map.keySet();
	} // end termSet

	/**
	 * Returns a <code>String</code> object representing this <code>Word</code>.
	 * 
	 * @return a string representation of this object.
	 */
	public String toString() {
		StringBuffer sb = new StringBuffer();

		Iterator it = map.entrySet().iterator();
		while (it.hasNext()) {
			Map.Entry me = (Map.Entry) it.next();
			sb.append(me.getValue());
			sb.append("\t");
			sb.append(me.getKey());
			sb.append("\n");
		}
		return sb.toString();
	} // end toString

	/**
	 * Writes the feature termIndex into the specified output stream in a format
	 * suitable for loading into a <code>Map</code> using the
	 * {@link #load(Reader) load} method.
	 * 
	 * @param out
	 *            a <code>Writer</code> object to provide the underlying stream.
	 * @throws IOException
	 *             if writing this feature termIndex to the specified output
	 *             stream throws an <code>IOException</code>.
	 */
	public void write(Writer out) throws IOException {
		PrintWriter pw = new PrintWriter(out);

		Iterator it = map.entrySet().iterator();
		logger.info("writing termIndex " + map.entrySet().size());

		while (it.hasNext()) {
			Map.Entry me = (Map.Entry) it.next();
			// token index
			pw.println(me.getValue() + "\t" + me.getKey());
		}
		pw.flush();
		pw.close();
	} // end write

	/**
	 * Reads the feature termIndex from the specified input stream.
	 * <p>
	 * This method processes input in terms of lines. A natural line of input is
	 * terminated either by a set of line terminator characters (\n or \r or
	 * \r\n) or by the end of the file. A natural line may be either a blank
	 * line, a comment line, or hold some part of a id-feature pair. Lines are
	 * read from the input stream until end of file is reached.
	 * <p>
	 * A natural line that contains only white space characters is considered
	 * blank and is ignored. A comment line has an ASCII '#' as its first
	 * non-white space character; comment lines are also ignored and do not
	 * encode id-feature information.
	 * <p>
	 * The id contains all of the characters in the line starting with the first
	 * non-white space character and up to, but not including, the first '\t'.
	 * All remaining characters on the line become part of the associated
	 * feature string; if there are no remaining characters, the feature is the
	 * empty string "".
	 * 
	 * @param in
	 *            a <code>Reader</code> object to provide the underlying stream.
	 * @throws IOException
	 *             if reading this feature termIndex from the specified input
	 *             stream throws an <code>IOException</code>.
	 */
	public void read(Reader in) throws IOException {
		logger.info("reading termIndex...");

		LineNumberReader lnr = new LineNumberReader(in);

		String line;
		String[] s;
		Integer id;
		while ((line = lnr.readLine()) != null) {
			line = line.trim();
			if (!line.startsWith("#")) {
				s = line.split("\t");
				// token index
				map.put(s[1], new Integer(s[0]));

				// SETTARE COUNT
				count++;
			}
		}
		lnr.close();

	} // end read

	/**
	 * Returns <code>TermIndex</code> object; only one <code>TermIndex</code>
	 * instance can exist.
	 * 
	 * @return <code>TermIndex</code> object
	 */
	public static synchronized TermIndex getTermIndex() {
		// logger.debug("TermIndex.getTermIndex");
		if (termIndex == null) {
			termIndex = new TermIndex();
		}

		return termIndex;
	} // end getTermIndex

	/**
	 * Returns <code>TermIndex</code> object; only one <code>TermIndex</code>
	 * instance can exist.
	 * 
	 * @return <code>TermIndex</code> object
	 */
	public static synchronized TermIndex getTermIndex(int count) {
		// logger.debug("TermIndex.getTermIndex");
		if (termIndex == null) {
			termIndex = new TermIndex(count);
		}

		return termIndex;
	} // end getTermIndex

} // end class TermIndex